/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

use std::{
    arch::aarch64::int32x2_t, borrow::Borrow, cell::{Cell, RefCell}, clone, ffi::{c_int, CString}, iter::StepBy, mem::ManuallyDrop, ops::Deref, os::raw::c_void, ptr::null_mut, rc::{Rc, Weak}, sync::{Arc, Condvar, Mutex}, thread::{self, sleep, JoinHandle}, time::{Duration, Instant}
};
use std::os::raw::c_char;
use std::borrow::Cow::Borrowed;
pub type HandleType = usize;
pub const INVALID_HANDLE: HandleType = 0;

mod sys;
use sys::libc::*;

pub use epoll::*;
pub use hilog_rust::*;

#[derive(Clone, Copy)]
enum RunningFlag {
    Running = 0,
    Error = 1,
    Stop = 2,
}

pub const LOG_LABEL: HiLogLabel = HiLogLabel {
    log_type: LogType::LogApp,
    domain: 1,
    tag: "IrondashRunLoop",
    priv_flag: false,
};

pub struct PlatformRunLoop {
    state: Arc<State>,
    running: Arc<Mutex<RunningFlag>>,
}

struct State {
    callbacks: Arc<Mutex<Callbacks>>,
    thread_id: PlatformThreadId,
}
unsafe impl Send for State {}
unsafe impl Sync for State {}

type SenderCallback = Box<dyn FnOnce() + Send>;

struct Callbacks {
    callbacks: Vec<SenderCallback>,
    fd: i32,
}

pub const NUM: usize = 16;

#[allow(unused_variables)]
impl PlatformRunLoop {
    pub fn new() -> Self {
        let state = State::get_instance();
        let mut running = RunningFlag::Stop;
        let running_ref = Arc::new(Mutex::new(running));
        Self {
            state,
            running: running_ref,
        }
    }

    pub fn poll_once(&self) {
    }

    pub fn unschedule(&self, handle: HandleType) {
        // TODO
    }

    #[must_use]
    pub fn schedule<F>(&self, in_time: Duration, callback: F) -> HandleType
    where
        F: FnOnce() + 'static,
    {
        // TODO
        1
    }

    pub fn run(&self) {
        hilog_debug!(LOG_LABEL, "irondash run_loop start run.");
        let state = State::get_instance().clone();
        let fd = state.callbacks.lock().unwrap().fd as u64;
        let epoll_fd = create(true).expect("irondash run_loop create epoll fd failed.");
        let mut event = Event {events: EPOLLIN, data: fd as u64};
        ctl(epoll_fd, ControlOptions::EPOLL_CTL_ADD, fd as i32, event)
            .expect("irondash run_loop epoll ctrl failed.");
        loop {
            *self.running.lock().unwrap() = RunningFlag::Running;
            let mut running_flag = 0;
            let mut buf = [0u8; 8];
            let mut event_array: [Event; NUM] = [Event {events: 0, data: 0}; NUM];
            let events_ref:&mut [Event] = &mut event_array;
            let ret = wait(epoll_fd, -1, events_ref).unwrap_or_else(|error| {
                hilog_error!(LOG_LABEL, "error occur in irondash run_loop wait. error kind: {:?}.", error.kind());
                *self.running.lock().unwrap() = RunningFlag::Error;
                0
            });
            match *self.running.lock().unwrap() {
                RunningFlag::Error => { continue; }
                RunningFlag::Stop => {
                    epoll::close(epoll_fd).expect("irondash run_loop epoll fd close failed");
                    break;
                }
                _ => { () }
            }
            match ret {
                0 => { hilog_error!(LOG_LABEL, "timeout in irondash run_loop wait."); }
                _ => {
                    let ret = ret as usize;
                    for i in 0..ret {
                        if event_array[i].data == fd as u64{
                            let ret = unsafe { read(event_array[0].data as i32, buf.as_mut_ptr() as *mut _, buf.len()) };
                            state.process_callbacks();
                        }
                    }
                }
            }
        }
    }

    pub fn stop(&self) {
    }

    //TODO (NEED DELETE)
    pub fn run_app(&self) {
    }

    //TODO (NEED DELETE)
    pub fn stop_app(&self) {
    }

    //TODO
    pub fn is_main_thread() -> bool {
        if State::get_instance().thread_id == get_system_thread_id() {
            true
        } else {
            false
        }
    }

    pub fn new_sender(&self) -> PlatformRunLoopSender {
        PlatformRunLoopSender {
            callbacks: Arc::downgrade(&self.state.callbacks),
        }
    }

    pub fn main_thread_fallback_sender() -> PlatformRunLoopSender {
        PlatformRunLoopSender {
            callbacks: Arc::downgrade(&State::get_instance().callbacks),
        }
    }
}
impl State {
    fn get_instance() -> Arc<State> {
        static mut INSTANCE: Option<Arc<State>> = None;
        unsafe {
            INSTANCE
                .get_or_insert_with(|| {
                    let flags = (EPOLL_CLOEXEC | EPOLL_NONBLOCK) as i32;
                    let initval: u32 = 0;
                    let event_fd = unsafe { eventfd(initval, flags) };
                    Arc::new(State {
                        callbacks: Arc::new(Mutex::new(Callbacks {
                            callbacks: Vec::new(),
                            fd: event_fd,
                        })),
                        thread_id: get_system_thread_id(),
                    })
                })
                .clone()
        }
    }
    fn process_callbacks(&self) {
        let callbacks: Vec<SenderCallback> = {
            let mut callbacks = self.callbacks.lock().unwrap();
            callbacks.callbacks.drain(0..).collect()
        };
        for c in callbacks {
            c()
        }
    }

    fn next_handle(&self) -> HandleType {
        1
    }

    pub fn schedule<F>(&self, in_time: Duration, callback: F) -> HandleType
    where
        F: FnOnce() + 'static,
    {
        1
    }

    pub fn unschedule(&self, handle: HandleType) {
    }
}

impl Drop for PlatformRunLoop {
    fn drop(&mut self) {
        { *self.running.lock().unwrap()= RunningFlag::Stop; }
        {
            let buf = [1u8; 8];
            let mut callbacks = self.state.callbacks.lock().unwrap();
            let ret = unsafe {
                write(callbacks.fd, buf.as_ptr() as *const _, buf.len())
            };
        }
    }
}

#[derive(Clone)]
pub struct PlatformRunLoopSender {
    callbacks: std::sync::Weak<Mutex<Callbacks>>,
}

#[allow(unused_variables)]
impl PlatformRunLoopSender {
    pub fn send<F>(&self, callback: F) -> bool
    where
        F: FnOnce() + 'static + Send,
    {
        if let Some(callbacks) = self.callbacks.upgrade()
        {
            let mut callbacks = callbacks.lock().unwrap();
            callbacks.callbacks.push(Box::new(callback));
            let buf = [1u8; 8];
            let ret = unsafe {
                write(callbacks.fd, buf.as_ptr() as *const _, buf.len())
            };
            true
        } else {
            false
        }
    }
}

pub(crate) type PlatformThreadId = usize;

pub(crate) fn get_system_thread_id() -> PlatformThreadId {
    unsafe { pthread_self() }
}